#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Box.h"

#include "Class.h"
#include "OrdColl.h"
#include "Math.h"
#include "Look.h"
#include "String.h"
#include "Buttons.h"
#include "TextItem.h"

struct rcinfo {
	int wd, ht, bs;
	int hfixed, vfixed;
};

//---- Box ---------------------------------------------------------------------

NewMetaImpl(Box,CompositeVObject, (T(colsrows), T(gap), TE(align)));

Box::Box(int id, const Point &cr, const Point &g, VObjAlign a)
	: colsrows(cr), gap(g), align(a), CompositeVObject(id, (SeqCollection*)0)
{
}

Box::Box(int id, const Point &cr, const Point &g, VObjAlign a, VObject *va_(vop), ...)
	: colsrows(cr), gap(g), align(a), CompositeVObject(id, (SeqCollection*)0)
{
	va_list ap;
	va_start(ap, va_(vop));
	SetItems(va_(vop), ap);
	va_end(ap);
}

Box::Box(int id, const Point &cr, const Point &g, VObjAlign a, SeqCollection *sc)
	: colsrows(cr), gap(g), align(a), CompositeVObject(id, sc)
{
}

Box::Box(int id, const Point &cr, const Point &g, VObjAlign a, va_list ap)
	: colsrows(cr), gap(g), align(a), CompositeVObject(id, ap)
{
}

void Box::SetAlign(VObjAlign a)
{
	align= a;
}

void Box::SetGap(const Point &g)
{
	gap= g;
}

void Box::SetColsRowsSize(const Point &cr)
{
	colsrows= cr;
}

Point Box::ColsRowsSize()
{
	int n= Size();
	if (colsrows.y <= 0 && colsrows.x <= 0) {
		int c= Math::Sqrt(n);
		return Point(c, (n-1)/c+1);
	}
	if (colsrows.y <= 0)
		return Point(colsrows.x, (n-1)/colsrows.x+1);
	if (colsrows.x <= 0)
		return Point((n-1)/colsrows.y+1, colsrows.y);
	return colsrows;
}

Metric Box::getMinSize(Point &rc, rcinfo *rci, bool natural)
{
	register int x, y;
	Metric m(-gap, 0);

	getGrid(rc, rci, natural);
	for (x= 0; x < rc.x; x++)
		m.extent.x+= rci[x].wd+gap.x;
	for (y= 0; y < rc.y; y++)
		m.extent.y+= rci[y].ht+gap.y;

	if (align & eVObjVBase)
		m.base= rci[0].bs;
	else if (Size() > 0)
		m.base= At(0)->Base();
	return m;
}

void Box::getGrid(Point &rc, rcinfo *rci, bool natural)
{
	VObjectIter next(this);
	register VObject *vop;
	register int x, y, hh, bb, maxwd= 0, maxht= 0, n;
	bool hexpand= align & eVObjHExpand,
		vexpand= (align & eVObjVExpand) && !(align & eVObjVBase);

	for (y= 0; y < rc.y; y++) {
		hh= bb= 0;
		for (x= 0; x < rc.x; x++) {
			if (vop= next()) {
				Metric m(vop->GetMinSize());
				if (natural) {
					Point e(vop->GetExtent());
					if (e.x > m.extent.x)
						m.extent.x= e.x;
					if (e.y > m.extent.y)
						m.extent.y= e.y;
				}
				if (align & eVObjVBase) {
					hh= Math::Max(hh, m.Base());
					int eb= m.Height()-m.Base();
					bb= Math::Max(bb, eb);
				} else {
					hh= Math::Max(hh, m.extent.y);
				}
				rci[x].wd= Math::Max(rci[x].wd, m.Width());
				maxwd= Math::Max(maxwd, m.Width());
				if (!hexpand || vop->TestFlag(eVObjHFixed))
					rci[x].hfixed++;
				if (!vexpand || vop->TestFlag(eVObjVFixed))
					rci[y].vfixed++;
			}
		
		}
		rci[y].ht= hh+bb;
		rci[y].bs= hh;
		maxht= Math::Max(maxht, hh+bb);
	}

	for (x= 0; x < rc.x; x++) {
		if (rci[x].hfixed >= rc.y)
			rci[x].hfixed= 1;
		else
			rci[x].hfixed= 0;
	}
	for (y= 0; y < rc.y; y++) {
		if (rci[y].vfixed >= rc.x)
			rci[y].vfixed= 1;
		else
			rci[y].vfixed= 0;
	}
	/*
	for (x= 0; x < rc.x; x++) {
		if (rci[x].hfixed > 0)
			rci[x].hfixed= 1;
		else
			rci[x].hfixed= 0;
	}
	for (y= 0; y < rc.y; y++) {
		if (rci[y].vfixed > 0)
			rci[y].vfixed= 1;
		else
			rci[y].vfixed= 0;
	}
	*/

	if ((align & eVObjHGapExpand) && rc.x > 1) {
		ResetFlag(eVObjHFixed);
	} else {
		for (n= x= 0; x < rc.x; x++)
			if (rci[x].hfixed)
				n++;
		SetFlag(eVObjHFixed, n >= rc.x);
	}

	if ((align & eVObjVGapExpand) && rc.y > 1) {
		ResetFlag(eVObjVFixed);
	} else {
		for (n= y= 0; y < rc.y; y++)
			if (rci[y].vfixed)
				n++;
		SetFlag(eVObjVFixed, n >= rc.y);
	}

	if (align & eVObjHEqual)
		for (x= 0; x < rc.x; x++)
			rci[x].wd= maxwd;
	if (align & eVObjVEqual)
		for (y= 0; y < rc.y; y++)
			rci[y].ht= maxht;
}

void Box::expand(Point &rc, rcinfo *rci, Point &g, Point &r)
{
	register int x, y;
	bool hagain, vagain;
	rcinfo *rci2= new rcinfo[Math::Max(rc.x, rc.y)];

	Point ne(GetExtent());
	Point oe(getMinSize(rc, rci, TRUE).extent);
	Point mine(getMinSize(rc, rci2, FALSE).extent);
	Point diff(ne-mine);

	g= gap;
	r= gPoint0;

	if (diff.x > 0) {
		if (align & eVObjHGapExpand) {
			if (rc.x > 1) {
				g.x= diff.x/(rc.x-1);
				r.x= diff.x-g.x*(rc.x-1);
				g.x+= gap.x;
			}
		}
	}

	if (diff.y > 0) {
		if (align & eVObjVGapExpand) {
			if (rc.y > 1) {
				g.y= diff.y/(rc.y-1);
				r.y= diff.y-g.y*(rc.y-1);
				g.y+= gap.y;
			}
		}
	}

	hagain= vagain= TRUE;

	for (int i= 0; i < 2; i++) {
		Point newe(ne), olde(oe);
		if (hagain && (align & eVObjHExpand)) {
			for (x= 0; x < rc.x; x++) {
				if (rci[x].hfixed) {  // col not hfixed
					newe.x-= rci[x].wd;
					olde.x-= rci[x].wd;
				}
				if (x < rc.x-1) {
					newe.x-= gap.x;
					olde.x-= gap.x;
				}
			}
		
			if (olde.x > 0) {
				int total= newe.x;
				hagain= FALSE;
				for (x= 0; x < rc.x; x++) {
					if (!rci[x].hfixed) {
						rci[x].wd= (rci[x].wd*newe.x)/olde.x;
						total-= rci[x].wd;
					}
				}
				for (x= 0; total > 0 && x < rc.x; x++) {
					if (!rci[x].hfixed) {
						rci[x].wd++;
						total--;
					}
				}
				for (x= 0; x < rc.x; x++) {
					if (!rci[x].hfixed && rci[x].wd < rci2[x].wd) {
						hagain= TRUE;
						break;
					}
				}
			}
		}

		if (vagain && (align & eVObjVExpand)) {
			for (y= 0; y < rc.y; y++) {
				if (rci[y].vfixed) {   // row not vfixed
					newe.y-= rci[y].ht;
					olde.y-= rci[y].ht;
				}
				if (y < rc.y-1) {
					newe.y-= gap.y;
					olde.y-= gap.y;
				}
			}
			if (olde.y > 0) {
				int total= newe.y;
				vagain= FALSE;
				for (y= 0; y < rc.y; y++) {
					if (!rci[y].vfixed) {
						rci[y].ht= (rci[y].ht*newe.y)/olde.y;
						total-= rci[y].ht;
					}
				}
				for (y= 0; total > 0 && y < rc.y; y++) {
					if (!rci[y].vfixed) {
						rci[y].ht++;
						total--;
					}
				}
				for (y= 0; y < rc.y; y++) {
					if (!rci[y].vfixed && rci[y].ht < rci2[y].ht) {
						vagain= TRUE;
						break;
					}
				}
			}
		}
		
		if (hagain)
			for (x= 0; x < rc.x; x++)
				rci[x].wd= rci2[x].wd;
		if (vagain)
			for (y= 0; y < rc.y; y++)
				rci[y].ht= rci2[y].ht;
	}

	delete rci2;
}

Metric Box::GetMinSize()
{
	Point rc(ColsRowsSize());
	rcinfo *rci= new rcinfo[Math::Max(rc.x, rc.y)];
	Metric m(getMinSize(rc, rci, FALSE));
	delete rci;
	return m;
}

void Box::SetOrigin(Point at)
{
	VObjectIter next(this);
	register VObject *vop;
	register int x, y;
	Point g, r, rr, a, rc(ColsRowsSize());
	rcinfo *rci= new rcinfo[Math::Max(rc.x, rc.y)];
		
	expand(rc, rci, g, r);

	VObject::SetOrigin(at);

	a.y= at.y;
	rr.y= r.y;
	for (y= 0; y < rc.y; y++) {
		a.x= at.x;
		rr.x= r.x;
		for (x= 0; x < rc.x; x++) {
			if (vop= next())
				vop->Align(a, Metric(rci[x].wd, rci[y].ht, rci[y].bs), align);
			a.x+= rci[x].wd + g.x;
			if (rr.x > 0) {
				a.x++;
				rr.x--;
			}
		}
		a.y+= rci[y].ht + g.y;
		if (rr.y > 0) {
			a.y++;
			rr.y--;
		}
	}
	delete rci;
}

void Box::SetExtent(Point ee)
{
	VObjectIter next(this);
	register VObject *vop;
	register x, y;
	Point g, r, rc(ColsRowsSize());
	rcinfo *rci= new rcinfo[Math::Max(rc.x, rc.y)];
	bool hexpand= align & (eVObjHExpand|eVObjHEqual),
		vexpand= (align & (eVObjVExpand|eVObjVEqual)) && !(align & eVObjVBase);

	Point e(getMinSize(rc, rci, FALSE).extent);
	if (!TestFlag(eVObjHFixed))
		e.x= ee.x;
	if (!TestFlag(eVObjVFixed) && !(align & eVObjVBase))
		e.y= ee.y;
	VObject::SetExtent(e);

	expand(rc, rci, g, r);

	for (y= 0; y < rc.y; y++) {
		for (x= 0; x < rc.x; x++) {
			if (vop= next()) {
				Point ms(vop->GetMinSize().extent);
				e= vop->GetExtent();
				if (e.x < ms.x || e.y < ms.y) {
					if (e.x < ms.x)
						e.x= ms.x;
					if (e.y < ms.y)
						e.y= ms.y;
				}
				if (hexpand && !vop->TestFlag(eVObjHFixed))
					e.x= rci[x].wd;
				if (vexpand && !vop->TestFlag(eVObjVFixed))
					e.y= rci[y].ht;
				vop->SetExtent(e);
			}
		}
	}

	delete rci;
}

OStream& Box::PrintOn(OStream &s)
{
	CompositeVObject::PrintOn(s);
	return s << colsrows SP << gap SP << (int)align SP;
}

IStream& Box::ReadFrom(IStream &s)
{
	CompositeVObject::ReadFrom(s);
	return s >> colsrows >> gap >> Enum(align);
}

int Box::FindVObject(VObject *v)
{
	VObject *vop;
	VObjectIter next(this);

	for (int i= 0; vop= next(); i++)
		if (v == vop)
			return i;
	return -1;
}

void Box::CalcResize(int where, Point d, rcinfo *minsize, rcinfo *normsize, rcinfo *result)
{
	Point rc(ColsRowsSize());
	int i;

	if (d.x) {  // vertical
		bool got= TRUE;
		int dd, gg= 0;
		if (d.x < 0) {
			dd= -d.x;
			while (dd > 0 && got) {
				got= FALSE;
				for (i= 0; i < where; i++) {
					if (!normsize[i].hfixed && result[i].wd > minsize[i].wd) {
						result[i].wd--;
						dd--;
						gg++;
						got= TRUE;
						break;
					}
				}
			}
			while (gg > 0) {
				for (i= where+1; i < rc.x; i++) {
					if (!normsize[i].hfixed) {
						result[i].wd++;
						gg--;
						break;
					}
				}
			}
		} else {
			dd= d.x;
			while (dd > 0 && got) {
				got= FALSE;
				for (i= where+1; i < rc.x; i++) {
					if (!normsize[i].hfixed && result[i].wd > minsize[i].wd) {
						result[i].wd--;
						dd--;
						gg++;
						got= TRUE;
						break;
					}
				}
			}
			while (gg > 0) {
				for (i= 0; i < where; i++) {
					if (!normsize[i].hfixed) {
						result[i].wd++;
						gg--;
						break;
					}
				}
			}
		}
	}

	if (d.y) { // hor
		bool got= TRUE;
		int dd, gg= 0;
		if (d.y < 0) {
			dd= -d.y;
			while (dd > 0 && got) {
				got= FALSE;
				for (i= 0; i < where; i++) {
					if (!normsize[i].vfixed && result[i].ht > minsize[i].ht) {
						result[i].ht--;
						dd--;
						gg++;
						got= TRUE;
						break;
					}
				}
			}
			while (gg > 0) {
				for (i= where+1; i < rc.y; i++) {
					if (!normsize[i].vfixed) {
						result[i].ht++;
						gg--;
						break;
					}
				}
			}
		} else {
			dd= d.y;
			while (dd > 0 && got) {
				got= FALSE;
				for (i= where+1; i < rc.y; i++) {
					if (!normsize[i].vfixed && result[i].ht > minsize[i].ht) {
						result[i].ht--;
						dd--;
						gg++;
						got= TRUE;
						break;
					}
				}
			}
			while (gg > 0) {
				for (i= 0; i < where; i++) {
					if (!normsize[i].vfixed) {
						result[i].ht++;
						gg--;
						break;
					}
				}
			}
		}
	}
}

void Box::Resize(rcinfo *newsize)
{
	register VObject *vop;
	register int x, y;
	VObjectIter next(this);
	Point at(GetOrigin()), a, rc(ColsRowsSize());
	Rectangle inval;

	bool hexpand= align & (eVObjHExpand|eVObjHEqual),
		vexpand= (align & (eVObjVExpand|eVObjVEqual)) && !(align & eVObjVBase);

	a.y= at.y;
	for (y= 0; y < rc.y; y++) {
		a.x= at.x;
		for (x= 0; x < rc.x; x++) {
			if (vop= next()) {
				Rectangle oldr(vop->contentRect);
				Point e(vop->GetExtent());
				if (e.x == 0 || e.y == 0)
					e= vop->GetMinSize().extent;
				if (hexpand && !vop->TestFlag(eVObjHFixed))
					e.x= newsize[x].wd;
				if (vexpand && !vop->TestFlag(eVObjVFixed))
					e.y= newsize[y].ht;
				vop->SetExtent(e);
				vop->Align(a, Metric(newsize[x].wd, newsize[y].ht, newsize[y].bs), align);
				if (vop->contentRect != oldr)
					inval.Merge(vop->contentRect);
			}
			a.x+= newsize[x].wd + gap.x;
		}
		a.y+= newsize[y].ht + gap.y;
	}

	InvalidateRect(inval);
}

//---- SplitMover --------------------------------------------------------------

class SplitMover: public Command {
	Rectangle constrainRect;
	Point delta, rc;
	Box *sbox;
	VObject *where;
	rcinfo *minsize, *result, *normsize;
	int iwhere;
public:
	SplitMover(Box *sb, VObject *w);
	~SplitMover();
	Command *TrackMouse(TrackPhase, Point, Point, Point);
	void TrackConstrain(Point ap, Point pp, Point *np);
	void TrackFeedback(Point, Point, bool on);
};

SplitMover::SplitMover(Box *sb, VObject *w)
{
	sbox= sb;
	constrainRect= sbox->contentRect;
	where= w;
	iwhere= sbox->FindVObject(where);
	rc= sbox->ColsRowsSize();
	int n= Math::Max(rc.x, rc.y);
	minsize= new rcinfo[n];
	sbox->getGrid(rc, minsize, FALSE);
	normsize= new rcinfo[n];
	sbox->getGrid(rc, normsize, TRUE);
}

SplitMover::~SplitMover()
{
	SafeDelete(minsize);
	SafeDelete(normsize);
	SafeDelete(result);
}

void SplitMover::TrackConstrain(Point ap, Point, Point *np)
{
	Rectangle r(where->contentRect);
	r.origin+= *np-ap;
	*np+= r.AmountToTranslateWithin(constrainRect);
}

Command *SplitMover::TrackMouse(TrackPhase tp, Point ap, Point, Point np)
{
	delta= np-ap;

	SafeDelete(result);
	int n= Math::Max(rc.x, rc.y);
	result= new rcinfo[n];
	MemCpy(result, normsize, n*sizeof(struct rcinfo));
	sbox->CalcResize(iwhere, delta, minsize, normsize, result);

	if (tp == eTrackRelease) {
		sbox->Resize(result);
		return gNoChanges;
	}
	return this;
}

void SplitMover::TrackFeedback(Point, Point, bool)
{
	Point gap(sbox->GetGap()),
		at(sbox->GetOrigin()),
		e(sbox->GetExtent());

	if (delta.x) {  // vertical
		for (int x= 0; x < rc.x; x++) {
			at.x+= result[x].wd;
			GrLine(at, at+Point(0, e.y));
			at.x+= gap.x;
		}
	}
		
	if (delta.y) { // hor
		for (int y= 0; y < rc.y; y++) {
			at.y+= result[y].ht;
			GrLine(at, at+Point(e.x, 0));
			at.y+= gap.y;
		}
	}
}

//---- HBox --------------------------------------------------------------------

NewMetaImpl0(HBox,Box);

HBox::HBox(const Point &g, VObjAlign a) : Box(cIdNone, Point(0,1), g, a)
{
}

HBox::HBox(const Point &g, VObjAlign a, VObject *va_(vop), ...)
					     : Box(cIdNone, Point(0,1), g, a)
{
	va_list ap;
	va_start(ap, va_(vop));
	SetItems(va_(vop), ap);
	va_end(ap);
}

HBox::HBox(const Point &g, VObjAlign a, SeqCollection *c)
				    : Box(cIdNone, Point(0,1), g, a, c)
{
}

HBox::HBox(VObject *va_(vop), ...) : Box(cIdNone, Point(0,1), gPoint2,
		(VObjAlign)(eVObjVTop|eVObjHExpand|eVObjVExpand))
{
	va_list ap;
	va_start(ap, va_(vop));
	SetItems(va_(vop), ap);
	va_end(ap);
}

//---- VBox --------------------------------------------------------------------

NewMetaImpl0(VBox,Box);

VBox::VBox(const Point &g, VObjAlign a) : Box(cIdNone, Point(1,0), g, a)
{
}

VBox::VBox(const Point &g, VObjAlign a, VObject *va_(vop), ...)
											: Box(cIdNone, Point(1,0), g, a)
{
	va_list ap;
	va_start(ap, va_(vop));
	SetItems(va_(vop), ap);
	va_end(ap);
}

VBox::VBox(const Point &g, VObjAlign a, SeqCollection *c)
											: Box(cIdNone, Point(1,0), g, a, c)
{
}

VBox::VBox(VObject *va_(vop), ...) : Box(cIdNone, Point(1,0), gPoint2,
			(VObjAlign)(eVObjHLeft|eVObjHExpand|eVObjVExpand))
{
	va_list ap;
	va_start(ap, va_(vop));
	SetItems(va_(vop), ap);
	va_end(ap);
}

//---- SplitHandle -------------------------------------------------------------

NewMetaImpl0(SplitHandle,VObject);

SplitHandle::SplitHandle()
{
}

Metric SplitHandle::GetMinSize()
{
	Metric m(gLook->SashLayout()->GetMinSize(this));
	Box *b= (Box*) GetContainer();
	if (b && b->IsKindOf(Box)) {
		Point rc(b->ColsRowsSize());
		if (rc.x > rc.y) {
			SetFlag(eVObjHFixed);
			m.extent.y*= 2;
		} else if (rc.x < rc.y) {
			SetFlag(eVObjVFixed);
			m.extent.x*= 2;
		}
	}
	return m;
}

void SplitHandle::Draw(Rectangle r)
{
	gLook->SashLayout()->Adorn(this, r, 0);
}

Command *SplitHandle::DoLeftButtonDownCommand(Point, Token, int)
{
	Box *sb= (Box*) GetContainer();
	if (sb && sb->IsKindOf(Box))
		return new SplitMover(sb, this);
	return gNoChanges;
}

//---- VObjectPair -------------------------------------------------------------

NewMetaImpl0(VObjectPair,HBox);

VObjectPair::VObjectPair(VObject *l, VObject *r, const Point &g)
						: HBox(g, eVObjVBase, l, r, 0)
{
}

//---- Expander ----------------------------------------------------------------

NewMetaImpl0(Expander, Box);

Expander::Expander(int id, Direction d, const Point &g)
				: Box(id, gPoint0, g, (VObjAlign)0)
{
	Init(d);
}

Expander::Expander(int id, Direction d, const Point &g, VObject *va_(vop), ...)
				: Box(id, gPoint0, g, (VObjAlign)0)
{
	va_list ap;
	va_start(ap,va_(vop));
	SetItems(va_(vop), ap);
	Init(d);
	va_end(ap);
}

Expander::Expander(int id, Direction d, const Point &g, SeqCollection *c)
					: Box(id, gPoint0, g, (VObjAlign)0, c)
{
	Init(d);
}

void Expander::Init(Direction d)
{
	if (d == eVert) {
		align= (VObjAlign)(eVObjHLeft|eVObjVExpand|eVObjHExpand);
		SetColsRowsSize(Point(1,0));
	} else {
		align= (VObjAlign)(eVObjVTop|eVObjVExpand|eVObjHExpand);
		SetColsRowsSize(Point(0,1));
	}
}

//---- HExpander ---------------------------------------------------------------

HExpander::HExpander(const Point &g, VObject *va_(vop), ...)
				: Expander(cIdNone, eHor, g, (SeqCollection*)0)
{
	va_list ap;
	va_start(ap,va_(vop));
	SetItems(va_(vop), ap);
	va_end(ap);
}

//---- VExpander ---------------------------------------------------------------

VExpander::VExpander(const Point &g, VObject *va_(vop), ...)
				: Expander(cIdNone, eVert, g, (SeqCollection*)0)
{
	va_list ap;
	va_start(ap,va_(vop));
	SetItems(va_(vop), ap);
	va_end(ap);
}

//---- Cluster -----------------------------------------------------------------

NewMetaImpl0(Cluster,Box);

Cluster::Cluster(int id, VObjAlign a, const Point &g) : Box(id, gPoint0, g, a)
{
	Init();
}

Cluster::Cluster(int id, VObjAlign a, const Point &g, SeqCollection *cp)
						: Box(id, gPoint0, g, a, cp)
{
	Init();
}

Cluster::Cluster(int id, VObjAlign a, const Point &g, VObject *va_(vop), ...)
						      : Box(id, gPoint0, g, a)
{
	va_list ap;
	va_start(ap, va_(vop));
	SetItems(va_(vop), ap);
	Init();
	va_end(ap);
}

Cluster::Cluster(int id, VObjAlign a, const Point &g, va_list ap)
						: Box(id, gPoint0, g, a, ap)
{
	Init();
}

void Cluster::Init()
{
	if (align & eVObjH)
		SetColsRowsSize(Point(1,0));
	if (align & eVObjV)
		SetColsRowsSize(Point(0,1));
}

//---- ManyOfCluster -----------------------------------------------------------

NewMetaImpl0(ManyOfCluster,Cluster);

ManyOfCluster::ManyOfCluster(int id, VObjAlign a, const Point &g, SeqCollection *cp)
						: Cluster(id, a, g, cp)
{
	SetFlag(eVObjVFixed);
}

ManyOfCluster::ManyOfCluster(int id, VObjAlign a, const Point &g, VObject *va_(vop), ...)
						: Cluster(id, a, g)
{
	va_list ap;
	va_start(ap,va_(vop));
	SetItems(va_(vop), ap);
	va_end(ap);
}

ManyOfCluster::ManyOfCluster(int id, VObject *va_(vop), ...) : Cluster(id, eVObjHLeft)
{
	va_list ap;
	va_start(ap,va_(vop));
	SetItems(va_(vop), ap);
	va_end(ap);
}

ManyOfCluster::ManyOfCluster(int id, VObjAlign a, const Point &g, char *va_(cp), ...)
						    : Cluster(id, a, g)
{
	char *t;
	va_list ap;
	va_start(ap,va_(cp));

	Add(new ToggleButton(id+0, va_(cp)));
	for (int i= 1; t= va_arg(ap, char*); i++)
		Add(new ToggleButton(id+i, t));
	va_end(ap);
}

ManyOfCluster::ManyOfCluster(int id, char *va_(cp), ...) : Cluster(id, eVObjHLeft)
{
	char *t;
	va_list ap;
	va_start(ap,va_(cp));

	Add(new ToggleButton(id+0, va_(cp)));
	for (int i= 1; t= va_arg(ap, char*); i++)
		Add(new ToggleButton(id+i, t));
	va_end(ap);
}

void ManyOfCluster::Control(int id, int p, void *v)
{
	if (p == cPartToggle)
		Cluster::Control(GetId(), id, v);
}

void ManyOfCluster::SetItem(int id, bool state)
{
	VObjectIter next(this);
	register VObject *dip;

	while (dip= next())
		if (dip->IsKindOf(StateButton) && dip->GetId() == id) {
			((StateButton*)dip)->SetValue(state ? 1 : 0);
			break;
		}
}

void ManyOfCluster::SetAllItems(bool state)
{
	VObjectIter next(this);
	register VObject *dip;

	while (dip= next())
		if (dip->IsKindOf(StateButton))
			((StateButton*)dip)->SetValue(state ? 1 : 0);
}

bool ManyOfCluster::IsItemOn(int id)
{
	VObjectIter next(this);
	register VObject *dip;

	while (dip= next())
		if (dip->IsKindOf(StateButton) && dip->GetId() == id)
			return ((StateButton*)dip)->GetValue() != 0;
	return FALSE;
}

//---- OneOfCluster ------------------------------------------------------------

NewMetaImpl0(OneOfCluster,Cluster);

OneOfCluster::OneOfCluster(int id, VObjAlign a, const Point &g, SeqCollection *cp)
						       : Cluster(id, a, g, cp)
{
	if (Size() > 0)
		SetItemOn(At(0)->GetId());
}

OneOfCluster::OneOfCluster(int id, VObjAlign a, const Point &g, VObject *va_(vop), ...)
						     : Cluster(id, a, g)
{
	va_list ap;
	va_start(ap,va_(vop));
	SetItems(va_(vop), ap);
	if (Size() > 0)
		SetItemOn(At(0)->GetId());
	va_end(ap);
}

OneOfCluster::OneOfCluster(int id, VObject *va_(vop), ...) : Cluster(id, eVObjHLeft)
{
	va_list ap;
	va_start(ap,va_(vop));
	SetItems(va_(vop), ap);
	if (Size() > 0)
		SetItemOn(At(0)->GetId());
	va_end(ap);
}

OneOfCluster::OneOfCluster(int id, VObjAlign a, const Point &g, char *va_(cp), ...)
							   : Cluster(id, a, g)
{
	char *t;
	va_list ap;
	va_start(ap,va_(cp));

	Add(new RadioButton(id+0, va_(cp)));
	for (int i= 1; t= va_arg(ap, char*); i++)
		Add(new RadioButton(id+i, t));
	SetItemOn(At(0)->GetId());
	va_end(ap);
}

OneOfCluster::OneOfCluster(int id, char *va_(cp), ...) : Cluster(id, eVObjHLeft)
{
	char *t;
	va_list ap;
	va_start(ap,va_(cp));

	Add(new RadioButton(id+0, va_(cp)));
	for (int i= 1; t= va_arg(ap, char*); i++)
		Add(new RadioButton(id+i, t));
	SetItemOn(At(0)->GetId());
	va_end(ap);
}

void OneOfCluster::SetValue(int id)
{
	VObjectIter next(this);
	register VObject *dip;

	while (dip= next())
		if (dip->IsKindOf(StateButton))
			((StateButton*)dip)->SetValue((int) dip->GetId() == id);
}

int OneOfCluster::GetValue()
{
	VObjectIter next(this);
	register VObject *dip;

	while (dip= next())
		if (dip->IsKindOf(StateButton))
			if (((StateButton*)dip)->GetValue())
				return dip->GetId();
	return cIdNone;
}

void OneOfCluster::Control(int id, int part, void *v)
{
	if (part == cPartToggle) {
		SetItemOn(id);
		Cluster::Control(GetId(), id, v);
	}
}

//---- Form --------------------------------------------------------------------

NewMetaImpl0(Form, Box);

Form::Form(int id, VObjAlign, const Point &g)
		: Box(id, Point(2,0), g, (VObjAlign)(eVObjHLeft|eVObjHExpand|eVObjVBase))
{
}

Form::Form(int id, VObjAlign, const Point &g, SeqCollection *cp)
		: Box(id, Point(2,0), g, (VObjAlign)(eVObjHLeft|eVObjHExpand|eVObjVBase), cp)
{
}

Form::Form(int id, VObjAlign, const Point &g, VObject *va_(vop), ...)
		: Box(id, Point(2,0), g, (VObjAlign)(eVObjHLeft|eVObjHExpand|eVObjVBase))
{
	va_list ap;
	va_start(ap, va_(vop));
	SetItems(va_(vop), ap);
	va_end(ap);
}

Form::Form(VObject *va_(vop), ...)
	: Box(cIdNone, Point(2,0), gPoint2, (VObjAlign)(eVObjHLeft|eVObjHExpand|eVObjVCenter))
{
	va_list ap;
	va_start(ap, va_(vop));
	SetItems(va_(vop), ap);
	va_end(ap);
}


Form::Form(int id, VObjAlign, const Point &g, char* va_(label), ...)
		: Box(id, Point(2,0), g, (VObjAlign)(eVObjHLeft|eVObjHExpand|eVObjVCenter))
{
	va_list ap;
	va_start(ap, va_(label));
	Init(va_(label), ap);
	va_end(ap);
}

Form::Form(char* va_(label), ...)
	: Box(cIdNone, Point(2,0), gPoint2, (VObjAlign)(eVObjHLeft|eVObjHExpand|eVObjVCenter))
{
	va_list ap;
	va_start(ap, va_(label));
	Init(va_(label), ap);
	va_end(ap);
}

void Form::Init(char *label, va_list ap)
{
	register VObject *vop;

	for (int i= 0; ; i++) {
		if (Math::Even(i)) {
			char *s= (i == 0) ? label : va_arg(ap, char*);
			if (s) {
				vop= new TextItem(s);
				//vop->SetFlag(eVObjHFixed);
			} else
				break;
		} else {
			if ((vop= va_arg(ap, VObject*)) == 0)
				break;
		}
		Add(vop);
	}
}

void Form::SetContainer(VObject *v)
{
	Box::SetContainer(v);

	VObjectIter next(this);
	Object *op;

	while (op= next()) {
		op->SetFlag(eVObjHFixed);
		op= next();
		if (op == 0)
			return;
	}
}
